package edu.harvard.eecs.airg.coloredtrails.controller;

import edu.harvard.eecs.airg.coloredtrails.shared.Utility;
import edu.harvard.eecs.airg.coloredtrails.shared.types.GameStatus;
import edu.harvard.eecs.airg.coloredtrails.server.ColoredTrailsServer;
import edu.harvard.eecs.airg.coloredtrails.server.AdminCommands;
import edu.harvard.eecs.airg.coloredtrails.server.CtrlCommands;
import edu.harvard.eecs.airg.coloredtrails.controller.events.GameStartEventListener;

import javax.jms.*;
import java.util.logging.Logger;
import java.util.Vector;
import java.net.*;
import java.io.File;
import java.io.IOException;

import org.apache.activemq.ActiveMQConnectionFactory;

public class ControlImpl implements Control, MessageListener, ExceptionListener {
    private Session session = null;
    private Topic topic = null;
    private Connection connection = null;

    private MessageProducer producer = null;
    private MessageConsumer consumer = null;


    protected Logger log = Logger.getAnonymousLogger();

    private Vector<GameStartEventListener> gameStartedListeners = new Vector<GameStartEventListener>();


    public ControlImpl(String host) {
        //host = "tcp://127.0.0.1:8080";
        //host = host.replaceAll("/", "//");
        log.info("Connecting Controller to Colored Trails Server..." + host);

        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(host);

        try {
            connection = factory.createConnection();
            connection.setExceptionListener(this);
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            topic = session.createTopic(ColoredTrailsServer.SERVER_NAME);

            /*
            * This selector to the consumer specifies that this client is to listen
            * for messages directed to admin client
            */

            consumer = session.createConsumer(topic, "(type = 'ADMIN_CLIENT')");
            consumer.setMessageListener(this);

            producer = session.createProducer(topic);
            producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
            connection.start();

        } catch (JMSException e) {
            log.severe("Creating Connection With the Server Failed:" + e);
            System.exit(-1);
        }
        log.info("Connected Controller to Colored Trails Server..." + host);
    }


    public void onException(JMSException arg) {
        log.severe("ADMIN: JMS Exception occured.  Shutting down." + arg);
        System.exit(-1);
    }


    public void onMessage(Message msg) {
        try {
            String msgName = msg.getStringProperty(CtrlCommands.MSG_NAME);
            String objectType = msg.getStringProperty(ColoredTrailsServer.OBJECTTYPE);
            if (objectType != null){
                log.info("objectType=" + objectType);
            }
            log.info("ADMIN: received a message:  " + msgName);
            if (msgName.equals(ColoredTrailsServer.GAMESTART) ){
                GameStatus g = new GameStatus((GameStatus) ((ObjectMessage) msg).getObject());
                log.info("gameid = " + g.getGameId());
                for (GameStartEventListener gl: gameStartedListeners){
                    gl.gameStarted(g);
                }

            } else if (msgName.equals(CtrlCommands.ADD_CONFIGURATION_REPLY)) {

                if (msg.getBooleanProperty(CtrlCommands.REPLY)) {
                    log.info("ADMIN: successfully added configuration");
                } else {
                    log.info("ADMIN: error adding configuration");
                }
            } else if (msgName.equals(CtrlCommands.NEW_GAME_REPLY)) {
                int gameId = msg.getIntProperty(CtrlCommands.NEW_GAME);
                log.info("Added new game:" + gameId);
            } else if (msgName.equals(ColoredTrailsServer.GAMESTART)) {
                for(GameStartEventListener gl : gameStartedListeners){
		        	gl.gameStarted((GameStatus) msg.getObjectProperty(ColoredTrailsServer.GAMESTATUS));
                }
            }

        } catch (JMSException e1) {
            e1.printStackTrace();
        }
    }


    public void sendConfig(String configdir, String className) {
        byte[] myclass;

        URI debug_uri = null;

        try {
            URL urls[] = {new File(configdir).toURI().toURL()};
            URLClassLoader u = URLClassLoader.newInstance(urls);

            URL path = u.getResource(className + ".class");

            debug_uri = path.toURI();
            myclass = Utility.getBytesFromFile(new File(path.toURI()));
            sendConfig(myclass, className);

        } catch (MalformedURLException e) {
            log.severe("Configuration directory URL invalid " + e.getMessage());
            return;
        } catch (URISyntaxException e) {
            log.severe("Configuration directory URL invalid " + e.getMessage());
            return;
        } catch (IOException e) {
            log.severe("Configuration directory URL invalid " + e.getMessage());
            return;
        } catch (IllegalArgumentException e) {
            log.severe("Configuration directory URL invalid. URI was: " + debug_uri);
            log.severe(e.getMessage());
            return;
        }
        catch (NullPointerException e) {
            log.severe("cannot find configuration class. Make sure it's compiled. " + e.getMessage());
            return;
        }
    }

    public void sendConfig(byte[] myclass, String className) {
        try {
            BytesMessage msg = session.createBytesMessage();
            msg.setStringProperty(ColoredTrailsServer.MSGTYPE, ColoredTrailsServer.ADMIN_MSG);
            msg.writeBytes(myclass);
            producer.send(msg);
            log.info("Admin: Sending config class");

            TextMessage txtMsg = session.createTextMessage();
            txtMsg.setStringProperty(ColoredTrailsServer.MSGTYPE, ColoredTrailsServer.ADMIN_MSG);
            txtMsg.setStringProperty(ColoredTrailsServer.COMMAND, AdminCommands.ADD_CONFIGURATION);
            txtMsg.setStringProperty(AdminCommands.CONFIGCLASS_NAME, className);
            producer.send(txtMsg);
            log.info("Admin: Sending config class name");

        } catch (JMSException e) {
            log.severe("Admin:Error sending config class to the Server:" + e);
            System.exit(-1);
        }
    }

    public void newGame(String ConfigClassName, String players) {
        try {
            TextMessage txtMsg = session.createTextMessage();
            txtMsg.setStringProperty(ColoredTrailsServer.MSGTYPE, ColoredTrailsServer.ADMIN_MSG);
            txtMsg.setStringProperty(ColoredTrailsServer.COMMAND, AdminCommands.NEW_GAME);
            txtMsg.setStringProperty(AdminCommands.CONFIGCLASS_NAME, ConfigClassName);
            txtMsg.setStringProperty(AdminCommands.LIST_PLAYERS, players);

            producer.send(txtMsg);
            log.info("Admin: Sending \"new game\" command");
        } catch (JMSException e) {
            log.severe("Admin:Error sending \"new game\" command to the Server:" + e);
            System.exit(-1);
        }
    }

    public synchronized void addGameStartEventListener(GameStartEventListener l) {
		gameStartedListeners.addElement(l);
	}

}

